﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

using Stanza.Silverlight.Media;

namespace Stanza.Silverlight.Controls
{
    [TemplatePart( Name = "colorPopupPanel", Type = typeof( ModalPopup ) )]
    [TemplatePart( Name = "chooseColorButton", Type = typeof( Button ) )]
    [TemplatePart( Name = "chooseColorPanelButton", Type = typeof( Button ) )]
    [TemplatePart( Name = "closeColorPanelButton", Type = typeof( Button ) )]
    [TemplatePart( Name = "colorEditBox", Type = typeof( TextBox ) )]
    [TemplatePart( Name = "hueMonitor", Type = typeof( Rectangle ) )]
    [TemplatePart( Name = "colorMonitor", Type = typeof( Rectangle ) )]
    [TemplatePart( Name = "opacityMonitor", Type = typeof( Rectangle ) )]
    [TemplatePart( Name = "originalColorRect", Type = typeof( Rectangle ) )]
    [TemplatePart( Name = "selectedColorRect", Type = typeof( Rectangle ) )]
    [TemplatePart( Name = "colorDisplayRect", Type = typeof( Rectangle ) )]
    [TemplatePart( Name = "colorRect", Type = typeof( Rectangle ) )]
    [TemplatePart( Name = "huePointer", Type = typeof( Canvas ) )]
    [TemplatePart( Name = "selectionPointer", Type = typeof( Canvas ) )]
    [TemplatePart( Name = "opacityPointer", Type = typeof( Canvas ) )]
    public class PopupColorPicker : ContentControl
    {
        public static readonly DependencyProperty SelectedColorProperty =
            DependencyProperty.Register( "SelectedColor", typeof( Color ), typeof( PopupColorPicker ),
                                         new PropertyMetadata( new PropertyChangedCallback( OnColorChanged ) ) );

        public Color SelectedColor
        {
            get { return (Color)GetValue( SelectedColorProperty ); }
            set { SetValue( SelectedColorProperty, value ); }
        }

        public event SelectedColorChangedHandler SelectedColorChanged;

        private ModalPopup colorPopupPanel;
        private Button chooseColorButton;
        private Button chooseColorPanelButton;
        private Button closeColorPanelButton;
        private TextBox colorEditBox;
        private Rectangle hueMonitor;
        private Rectangle colorMonitor;
        private Rectangle opacityMonitor;
        private Rectangle originalColorRect;
        private Rectangle selectedColorRect;
        private Rectangle colorDisplayRect;
        private Rectangle colorRect;
        private Canvas huePointer;
        private Canvas selectionPointer;
        private Canvas opacityPointer;

        private bool textBoxHasFocus = false;
        private bool isChangingColor = false;
        private bool isChangingHue = false;
        private bool isChangingOpacity = false;
        private const int selectorSize = 10;
        private Color originalColor = Colors.Black;
        private NumericRangeTranslation currentHue;
        private NumericRangeTranslation currentSaturation;
        private NumericRangeTranslation currentValue;
        private NumericRangeTranslation currentOpacity;

        public PopupColorPicker()
        {
            DefaultStyleKey = typeof( PopupColorPicker );
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            if( !DesignerTools.IsInDesignMode )
            {
                colorPopupPanel = GetTemplateChild( "colorPopupPanel" ) as ModalPopup;
                chooseColorButton = GetTemplateChild( "chooseColorButton" ) as Button;
                chooseColorPanelButton = GetTemplateChild( "chooseColorPanelButton" ) as Button;
                closeColorPanelButton = GetTemplateChild( "closeColorPanelButton" ) as Button;
                colorEditBox = GetTemplateChild( "colorEditBox" ) as TextBox;
                hueMonitor = GetTemplateChild( "hueMonitor" ) as Rectangle;
                colorMonitor = GetTemplateChild( "colorMonitor" ) as Rectangle;
                opacityMonitor = GetTemplateChild( "opacityMonitor" ) as Rectangle;
                originalColorRect = GetTemplateChild( "originalColorRect" ) as Rectangle;
                selectedColorRect = GetTemplateChild( "selectedColorRect" ) as Rectangle;
                colorDisplayRect = GetTemplateChild( "colorDisplayRect" ) as Rectangle;
                colorRect = GetTemplateChild( "colorRect" ) as Rectangle;
                huePointer = GetTemplateChild( "huePointer" ) as Canvas;
                selectionPointer = GetTemplateChild( "selectionPointer" ) as Canvas;
                opacityPointer = GetTemplateChild( "opacityPointer" ) as Canvas;

                chooseColorButton.Click += delegate( object sender, RoutedEventArgs e ) {
                    OpenColorPanel();
                };
                chooseColorPanelButton.Click += delegate( object sender, RoutedEventArgs e ) {
                    CloseColorPanel();
                };
                closeColorPanelButton.Click += delegate( object sender, RoutedEventArgs e ) {
                    SelectedColor = originalColor;
                    CloseColorPanel();
                };

                colorEditBox.TextChanged += new TextChangedEventHandler( colorEditBox_TextChanged );
                colorEditBox.GotFocus += delegate( object sender, RoutedEventArgs e ) {
                    textBoxHasFocus = true;
                };
                colorEditBox.LostFocus += delegate( object sender, RoutedEventArgs e ) {
                    textBoxHasFocus = false;
                };

                colorMonitor.MouseLeftButtonDown += delegate( object sender, MouseButtonEventArgs e ) {
                    isChangingColor = true;
                    Point pt = GetRelativePosition( e, colorMonitor );
                    SetSelectionPoint( pt.X, pt.Y );
                    UpdatePanelSelectedColor();
                };
                colorMonitor.MouseLeftButtonUp += delegate( object sender, MouseButtonEventArgs e ) {
                    isChangingColor = false;
                };
                colorMonitor.MouseMove += delegate( object sender, MouseEventArgs e ) {
                    if( isChangingColor )
                    {
                        Point pt = GetRelativePosition( e, colorMonitor );
                        SetSelectionPoint( pt.X, pt.Y );
                        UpdatePanelSelectedColor();
                    }
                };
                colorMonitor.MouseLeave += delegate( object sender, MouseEventArgs e ) {
                    isChangingColor = false;
                };

                hueMonitor.MouseLeftButtonDown += delegate( object sender, MouseButtonEventArgs e ) {
                    isChangingHue = true;
                    Point pt = GetRelativePosition( e, hueMonitor );
                    SetHue( pt.Y );
                    UpdatePanelSelectedColor();
                };
                hueMonitor.MouseLeftButtonUp += delegate( object sender, MouseButtonEventArgs e ) {
                    isChangingHue = false;
                };
                hueMonitor.MouseMove += delegate( object sender, MouseEventArgs e ) {
                    if( isChangingHue )
                    {
                        Point pt = GetRelativePosition( e, hueMonitor );
                        SetHue( pt.Y );
                        UpdatePanelSelectedColor();
                    }
                };
                hueMonitor.MouseLeave += delegate( object sender, MouseEventArgs e ) {
                    isChangingHue = false;
                };

                opacityMonitor.MouseLeftButtonDown += delegate( object sender, MouseButtonEventArgs e ) {
                    isChangingOpacity = true;
                    Point pt = GetRelativePosition( e, opacityMonitor );
                    SetOpacity( pt.Y );
                    UpdatePanelSelectedColor();
                };
                opacityMonitor.MouseLeftButtonUp += delegate( object sender, MouseButtonEventArgs e ) {
                    isChangingOpacity = false;
                };
                opacityMonitor.MouseMove += delegate( object sender, MouseEventArgs e ) {
                    if( isChangingOpacity )
                    {
                        Point pt = GetRelativePosition( e, opacityMonitor );
                        SetOpacity( pt.Y );
                        UpdatePanelSelectedColor();
                    }
                };
                opacityMonitor.MouseLeave += delegate( object sender, MouseEventArgs e ) {
                    isChangingOpacity = false;
                };

                hueMonitor.SizeChanged += delegate( object sender, SizeChangedEventArgs e ) {
                    currentHue = new NumericRangeTranslation( hueMonitor.ActualHeight, HSVColor.Degrees );
                    InitializeColorPanel();
                };
                colorMonitor.SizeChanged += delegate( object sender, SizeChangedEventArgs e ) {
                    currentSaturation = new NumericRangeTranslation( colorMonitor.ActualWidth, HSVColor.MaxPercent );
                    currentValue = new NumericRangeTranslation( colorMonitor.ActualHeight, HSVColor.MaxPercent, true );
                    InitializeColorPanel();
                };
                opacityMonitor.SizeChanged += delegate( object sender, SizeChangedEventArgs e ) {
                    currentOpacity = new NumericRangeTranslation( opacityMonitor.ActualHeight, 255, true );
                    InitializeColorPanel();
                };

                UpdateSelectedColor( SelectedColor );
            }
            else
            {
                colorPopupPanel = GetTemplateChild( "colorPopupPanel" ) as ModalPopup;
                colorPopupPanel.SetAsEmbeddedControl();
            }
        }

        Point GetRelativePosition( MouseEventArgs e, Rectangle monitorRect )
        {
            // Not sure why e.GetPosition( monitorRect ) is not working, so do it this way instead:
            Point mousePt = e.GetPosition( null );
            Point rectPt = UIElementTools.GetAbsolutePosition( monitorRect );
            mousePt.X -= rectPt.X;
            mousePt.Y -= rectPt.Y;
            return mousePt;
        }

        void OpenColorPanel()
        {
            Point pt = chooseColorButton.GetAbsolutePosition();
            pt.Y -= colorPopupPanel.Height;
            colorPopupPanel.OpenPopup( pt );
            InitializeColorPanel();
        }

        void InitializeColorPanel()
        {
            if( ( currentHue != null ) &&
                ( currentSaturation != null ) &&
                ( currentValue != null ) &&
                ( currentOpacity != null ) )
            {
                originalColor = SelectedColor;
                originalColorRect.Fill = new SolidColorBrush( originalColor );

                HSVColor hsvColor = HSVColor.FromColor( originalColor );
                currentHue.ItemValue = hsvColor.Hue;
                currentSaturation.ItemValue = hsvColor.Saturation;
                currentValue.ItemValue = hsvColor.Value;
                currentOpacity.ItemValue = originalColor.A;

                SetHue( currentHue.ScreenValue );
                SetSelectionPoint( currentSaturation.ScreenValue, currentValue.ScreenValue );
                SetOpacity( currentOpacity.ScreenValue );
                UpdatePanelSelectedColor();
            }
        }

        void CloseColorPanel()
        {
            currentHue = currentSaturation = currentValue = currentOpacity = null;
            colorPopupPanel.ClosePopup();
        }

        void colorEditBox_TextChanged( object sender, TextChangedEventArgs e )
        {
            if( textBoxHasFocus )
                if( ColorTools.IsValidColorString( colorEditBox.Text ) )
                {
                    Color colorValue = ColorTools.ConvertStringToColor( colorEditBox.Text );
                    if( SelectedColor != colorValue )
                        SelectedColor = colorValue;
                }
        }

        private static void OnColorChanged( DependencyObject d, DependencyPropertyChangedEventArgs args )
        {
            PopupColorPicker colorPicker = d as PopupColorPicker;
            if( colorPicker != null )
                colorPicker.UpdateSelectedColor( (Color)args.NewValue );
        }

        private void UpdateSelectedColor( Color colorValue )
        {
            if( !DesignerTools.IsInDesignMode && ( colorEditBox != null ) && ( colorDisplayRect != null ) )
            {
                if( !textBoxHasFocus )
                    colorEditBox.Text = ColorTools.ConvertColorToString( colorValue );
                colorDisplayRect.Fill = new SolidColorBrush( colorValue );
            }
            if( SelectedColorChanged != null )
                SelectedColorChanged( this, new SelectedColorChangedArgs( SelectedColor ) );
        }

        private void UpdatePanelSelectedColor()
        {
            Color newColor = HSVColor.ToColor( new HSVColor( currentHue.ItemValue, currentSaturation.ItemValue, currentValue.ItemValue ) );
            newColor.A = Convert.ToByte( currentOpacity.ItemValue );
            selectedColorRect.Fill = new SolidColorBrush( newColor );
            SelectedColor = newColor;
        }

        private void SetHue( double y )
        {
            currentHue.ScreenValue = y;
            Canvas.SetTop( huePointer, y - selectorSize / 2 );
            colorRect.Fill = new SolidColorBrush( HSVColor.GetColorFromHue( currentHue.ItemValue ) );
        }

        private void SetOpacity( double y )
        {
            currentOpacity.ScreenValue = y;
            Canvas.SetTop( opacityPointer, y - selectorSize / 2 );
        }

        private void SetSelectionPoint( double x, double y )
        {
            currentSaturation.ScreenValue = x;
            currentValue.ScreenValue = y;
            Canvas.SetLeft( selectionPointer, x - selectorSize / 2 );
            Canvas.SetTop( selectionPointer, y - selectorSize / 2 );
        }
    }
}
